package com.pocket.demo.base.api;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.pocket.demo.base.enums.MsgCodeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;

import java.io.Serializable;
import java.util.Optional;

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "统一返回消息体")
public class R<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    @Schema(name = "状态码", requiredMode = Schema.RequiredMode.REQUIRED)
    private Long code;

    @Schema(name = "返回消息", requiredMode = Schema.RequiredMode.REQUIRED)
    private String msg;

    // 属性为 NULL 不序列化
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @Schema(name = "接口数据")
    private T data;

    // 属性为 空（""） 或 NULL 都不序列化
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @Schema(name = "分页数据")
    private PageResult page;

    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @Schema(name = "状态码信息", requiredMode = Schema.RequiredMode.REQUIRED)
    private String error;

    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @Schema(name = "debug日志", requiredMode = Schema.RequiredMode.REQUIRED)
    private String debug;

    @JsonIgnore
    @Schema(name = "是否成功", requiredMode = Schema.RequiredMode.REQUIRED)
    private boolean success;

    private R(Long code, String msg) {
        this(code, msg, null, null, null, null);
    }

    private R(Long code, String msg, T data) {
        this(code, msg, null, null, data, null);
    }

    private R(Long code, String msg, T data, PageResult page) {
        this(code, msg, null, null, data, page);
    }

    private R(Long code, String msg, String debug, String error) {
        this(code, msg, debug, error, null, null);
    }

    private R(Long code, String msg, String debug, String error, T data, PageResult page) {
        this.code = code;
        this.msg = msg;
        this.debug = debug;
        this.error = error;
        this.data = data;
        this.page = page;
        this.success = MsgCodeEnum.SUCCESS.getCode().equals(code);
    }

    /**
     * 判断返回是否为成功
     *
     * @param result Result
     * @return 是否成功
     */
    public static boolean isSuccess(@Nullable R<?> result) {
        return Optional.ofNullable(result)
                .map(x -> ObjectUtils.nullSafeEquals(MsgCodeEnum.SUCCESS.getCode(), x.code))
                .orElse(Boolean.FALSE);
    }

    /**
     * 判断返回是否异常
     *
     * @param result Result
     * @return 是否成功
     */
    public static boolean isNotSuccess(@Nullable R<?> result) {
        return !R.isSuccess(result);
    }

    /**
     * 成功响应，无返回数据
     * @param code  状态码
     * @param msg   状态描述
     * @param <T>    T 泛型标记
     * @return R
     */
    public static <T> R<T> success(Long code, String msg) {
        return new R<>(code, msg);
    }

    /**
     * 成功响应，带返回数据
     *
     * @param code  返回码
     * @param msg   返回信息
     * @param data  数据
     * @param <T>  T 泛型标记
     * @return R
     */
    public static <T> R<T> success(Long code, String msg, T data) {
        return new R<>(code, msg, data);
    }


    /**
     * 成功响应，带返回数据 && 分页数据
     *
     * @param code  状态码
     * @param msg   状态描述
     * @param <T>   T 泛型标记
     * @param page  分页对象
     * @return R
     */
    public static <T> R<T> success(Long code, String msg, T data, PageResult page) {
        return new R<>(code, msg, data, page);
    }

    /**
     * 异常响应，带错误异常信息
     *
     * @param code   状态码
     * @param msg    状态描述
     * @param error  错误描述
     * @param <T>    T 泛型标记
     * @return R
     */
    public static <T> R<T> fail(Long code, String msg, String error) {
        return new R<>(code, msg, null, error);
    }

    /**
     * 异常响应，带错误异常信息 && 堆栈信息
     *
     * @param code      状态码
     * @param error     状态描述
     * @param debug     错误详情
     * @param msg       错误详情
     * @param <T>       T 泛型标记
     * @return R
     */
    public static <T> R<T> fail(Long code, String msg, String debug, String error) {
        return new R<>(code, msg, debug, error);
    }

}
